<template>
  <!-- 联合材质 -->
  <!-- 因为MeshDepthMaterial没有color属性，所以不能动态的改变材质外部的颜色，但是可以通过SceneUtils工具类的其中一个方法createMultiMaterialObject进行材质联合，来实现颜色的添加。 -->
  <div class="multi">
    <div id="scene"></div>
    <div class="controls-box">
      <section>
        <el-row>
          <div v-for="(item, key) in properties" :key="key">
            <div v-if="item && item.name != undefined">
              <el-col :span="8">
                <span class="vertice-span">{{ item.name }}</span>
              </el-col>
              <el-col :span="13">
                <el-slider
                  v-model="item.value"
                  :min="item.min"
                  :max="item.max"
                  :step="item.step"
                  :format-tooltip="formatTooltip"
                ></el-slider>
              </el-col>
              <el-col :span="3">
                <span class="vertice-span">{{ item.value }}</span>
              </el-col>
            </div>
          </div>
        </el-row>
        <el-row>
          <el-button
            type="primary"
            class="controls-button"
            size="mini"
            @click="addCube"
            >addCube</el-button
          >
          <el-button
            type="primary"
            class="controls-button"
            size="mini"
            @click="removeCube"
            >removeCube</el-button
          >
        </el-row>
      </section>
    </div>
  </div>
</template>

<script>
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
// import SceneUtils from "three/examples/jsm/utils/SceneUtils";
import { createMultiMaterialObject } from "three/examples/jsm/utils/SceneUtils";

let scene;
let camera;
let renderer;
let controls;
export default {
  data() {
    return {
      properties: {
        near: {
          name: "near",
          value: 66,
          min: 0,
          max: 500,
          step: 1,
        },
        far: {
          name: "far",
          value: 230,
          min: 50,
          max: 1000,
          step: 1,
        },
        speed: {
          name: "speed",
          value: 0.02,
          min: 0,
          max: 0.5,
          step: 0.01,
        },
      },
      defaultCubeNum: 0,
    };
  },
  mounted() {
    this.init();
  },
  methods: {
    formatTooltip(val) {
      return val;
    },

    // 初始化
    init() {
      this.createScene(); // 创建场景
      this.createLight(); // 创建光源
      this.createCamera(); // 创建相机
      this.createRender(); // 创建渲染器
      this.createControls(); // 创建控件对象
      this.render(); // 渲染
      this.initCube(); // 初始化方块
    },
    // 创建场景
    createScene() {
      scene = new THREE.Scene();
    },

    // 创建光源
    createLight() {
      // 环境光
      const ambientLight = new THREE.AmbientLight(0x000000); // 创建环境光
      scene.add(ambientLight); // 将环境光添加到场景
    },
    // 创建相机
    createCamera() {
      const element = document.getElementById("scene");
      const k = element.clientWidth / element.clientHeight; // 窗口宽高比
      // PerspectiveCamera( fov, aspect, near, far )
      camera = new THREE.PerspectiveCamera(45, k, 20, 1000);
      camera.position.set(-80, 60, 40); // 设置相机位置
      camera.lookAt(new THREE.Vector3(0, 0, 0)); // 设置相机方向
      scene.add(camera);
    },
    initCube() {
      while (this.defaultCubeNum < 100) {
        this.addCube();
        this.defaultCubeNum++;
      }
    },
    removeCube() {
      const allChildren = scene.children;
      const lastObject = allChildren[allChildren.length - 1];
      if (lastObject instanceof THREE.Mesh) {
        scene.remove(lastObject);
      }
    },
    addCube() {
      const cubeSize = Math.ceil(3 + Math.random() * 3);
      const cubeGeometry = new THREE.BoxGeometry(cubeSize, cubeSize, cubeSize);
      // 定义网格材质
      //MeshDepthMaterial没有一个属性来设置物体的颜色，都是由材质的默认属性决定的，但是three.js库提供了一种方法，通过创建联合材质来给深度材质设置颜色
      //创建两种材质
      const cubeMaterial = new THREE.MeshDepthMaterial();
      //MeshBasicMaterial需要把transparent属性设置为true，设置为true物体颜色就会和背景色进行融合
      const colorMaterial = new THREE.MeshBasicMaterial({
        color: 0x00ff00,
        transparent: true,
        blending: THREE.MultiplyBlending,
      });

      // 定义 cube 网格
      //创建联合网格对象，通过createMultiMaterialObject创建的网格对象，是一个网格组，两个几何对象一样的mesh对象，不过一个是基础材质一个是深度材质
      const meshMaterial = [colorMaterial, cubeMaterial];
      const cube = createMultiMaterialObject(cubeGeometry, meshMaterial);
      cube.children[1].scale.set(0.99, 0.99, 0.99); // 缩小 cubeMaterial 的网格对象，避免闪烁问题
      cube.castShadow = true;
      cube.position.x = -60 + Math.round(Math.random() * 100);
      cube.position.y = Math.round(Math.random() * 10);
      cube.position.z = -100 + Math.round(Math.random() * 150);
      // 默认加入 cube
      scene.add(cube);
    },
    // 创建渲染器
    createRender() {
      const element = document.getElementById("scene");
      renderer = new THREE.WebGLRenderer();
      renderer.setSize(element.clientWidth, element.clientHeight); // 设置渲染区域尺寸
      renderer.shadowMap.enabled = true; // 显示阴影
      renderer.setClearColor(0x000000, 1); // 设置背景颜色
      element.appendChild(renderer.domElement);
    },

    // 更新属性
    updateFun() {
      camera.near = this.properties.near.value;
      camera.far = this.properties.far.value;
      //在更新完相机的属性far和near后要调用camera.updateProjectionMatrix()
      camera.updateProjectionMatrix();
      const THIS = this;
      scene.traverse((e) => {
        if (e instanceof THREE.Mesh) {
          e.rotation.x += THIS.properties.speed.value;
          e.rotation.y += THIS.properties.speed.value;
          e.rotation.z += THIS.properties.speed.value;
        }
      });
    },
    render() {
      this.updateFun();
      renderer.render(scene, camera);
      requestAnimationFrame(this.render);
    },
    // 创建控件对象
    createControls() {
      controls = new OrbitControls(camera, renderer.domElement);
      controls.target = new THREE.Vector3(0, 0, 0);
    },
  },
};
</script>

<style>
.multi {
  width: 100%;
  height: 100%;
  position: relative;
}
#scene {
  width: 100%;
  height: 100%;
}
.controls-box {
  position: absolute;
  right: 5px;
  top: 5px;
  width: 300px;
  padding: 10px;
  background-color: #fff;
  border: 1px solid #c3c3c3;
}
.vertice-span {
  line-height: 38px;
  padding: 0 2px 0 10px;
}
</style>

